package com.wwu.common.minio;

import io.minio.*;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;

/**
 * MinIo工具类
 * @author 一蓑烟雨
 * @version 1.0.0
 * @date 2022-09-02 12:09
 **/
@Slf4j
@Component
public class MinioUtils {
    @Resource
    private MinioClient minioClient;

    /****************************************************Bucket存储桶操作***********************************************/
    /**
     *  创建存储桶
     * @param bucketName: 存储桶名称
     * @return: Boolean
     * @author 一蓑烟雨
     * @date 2022/9/2 12:22
     */
    public Boolean createBucket(String bucketName) {
        if (!bucketExists(bucketName)) {
            try {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
                return true;
            } catch (Exception e) {
                log.error("MinIO创建存储桶异常：{}", e);
            }
        }
        return false;
    }

    /**
     * 判断存储桶是否存在,true表示存在，false表示不存在
     * @param bucketName: 存储桶名称
     * @return: boolean
     * @author 一蓑烟雨
     * @date 2022/9/2 14:55
     */
    private boolean bucketExists(String bucketName) {
        try {
            return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        } catch (Exception e) {
            log.error("判断MinIO存储桶是否存在异常：{}", e);
        }
        return false;
    }

    /**
     * 获得所有存储桶列表
     * @return: java.util.List<io.minio.messages.Bucket>
     * @author 一蓑烟雨
     * @date 2022/9/2 17:36
     */
    public List<Bucket> getAllBuckets(){
        try {
            return  minioClient.listBuckets();
        } catch (Exception e) {
            log.error("获取MinIO存储桶列表异常：{}", e);
        }
        return  null;
    }

    /**
     * 获取指定存储桶相关信息
     * @param bucketName: 存储桶名称
     * @return: java.util.Optional<io.minio.messages.Bucket>
     * @author 一蓑烟雨
     * @date 2022/9/2 17:39
     */
    public Optional<Bucket> getBucket(String bucketName){
        return getAllBuckets().stream().filter(
                bucket -> bucket.name().equals(bucketName))
                .findFirst();
    }

    /**
     * 删除存储桶
     * @param bucketName: 存储桶名称
     * @return: Boolean
     * @author 一蓑烟雨
     * @date 2022/9/2 17:42
     */
    public Boolean removeBucket(String bucketName){
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            return true;
        } catch (Exception e) {
            log.error("删除存储桶异常：{}", e);
        }
        return false;
    }

    /****************************************************Bucket存储桶文件操作********************************************/

    /**
     * 判断指指定文件是否存在,true表示存在，false表示不存在
     * @param bucketName: 存储桶名称
     * @param objectName: 文件名称
     * @return: boolean
     * @author 一蓑烟雨
     * @date 2022/9/2 17:52
     */
    public boolean objectExists(String bucketName,String objectName){
        boolean exist = true;
        try {
            minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            log.error("判断MinIO存储桶文件是否存在异常：{}", e);
            exist = false;
        }
        return exist;
    }

    /**
     * 判断指定文件夹是否存在,true表示存在，false表示不存在
     * @param bucketName: 存储桶名称
     * @param objectName: 文件夹名称
     * @return: boolean
     * @author 一蓑烟雨
     * @date 2022/9/2 18:06
     */
    public boolean folderExists(String bucketName,String objectName){
        boolean exist = true;
        try {
            Iterable<Result<Item>> results = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());
            /** 遍历Bucket对象 */
            for (Result<Item> result : results){
                Item item = result.get();
                if(item.isDir() && objectName.equals(item.objectName())){
                    exist = true;
                }
            }
        } catch (Exception e) {
            log.error("判断存储桶文件夹是否存在异常：{}", e);
            exist = false;
        }
        return exist;
    }

    /**
     * 根据文件前缀查询文件
     * @param bucketName: 存储桶名称
     * @param prefix: 文件前缀
     * @param recursive: 是否使用递归查询
     * @return: java.util.List<io.minio.messages.Item>
     * @author 一蓑烟雨
     * @date 2022/9/3 8:04
     */
    public List<Item> getObjectsByPrefix(String bucketName,String prefix,boolean recursive){
        List<Item> objs = new ArrayList<>();
        try {
            Iterable<Result<Item>> objIterator = minioClient.listObjects(
                    ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
            if(objIterator != null){
                for (Result<Item> obj : objIterator){
                    Item item = obj.get();
                    objs.add(item);
                }
            }
        } catch (Exception e) {
            log.error("根据文件前缀查询指定存储桶中的文件异常：{}", e);
        }
        return objs;
    }

    /**
     * 获取指定文件流
     * @param bucketName: 存储桶名称
     * @param objectName: 对象名称
     * @return: java.io.InputStream
     * @author 一蓑烟雨
     * @date 2022/9/3 8:07
     */
    public InputStream getObjectStream(String bucketName,String objectName){
        try {
            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            log.error("获取指定存储桶中的指定文件流异常：{}", e);
        }
        return null;
    }

    /**
     * 获取指定文件流(断点下载)
     * @param bucketName: 存储桶名称
     * @param objectName: 对象名称
     * @param offset: 起始节点位置
     * @param length: 读取的长度
     * @return: java.io.InputStream
     * @author 一蓑烟雨
     * @date 2022/9/3 8:12
     */
    public InputStream getObjectStream(String bucketName,String objectName,long offset,long length){
        try {
            return minioClient.getObject(GetObjectArgs.builder()
                    .bucket(bucketName).object(objectName)
                    .offset(offset).length(length).build());
        } catch (Exception e) {
            log.error("获取指定存储桶中的指定文件流异常：{}", e);
        }
        return null;
    }

    /**
     * 获取指定存储桶中的文件列表
     * @param bucketName: 存储桶名称
     * @param prefix:  文件前缀
     * @param recursive: 是否递归查询
     * @return: java.lang.Iterable<io.minio.Result<io.minio.messages.Item>>
     * @author 一蓑烟雨
     * @date 2022/9/3 8:17
     */
    public Iterable<Result<Item>> listObjects(String bucketName,String prefix,boolean recursive){
        try {
            return minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(bucketName).prefix(prefix).recursive(recursive)
                    .build());
        } catch (Exception e) {
            log.error("获取指定存储桶中的文件列表异常：{}", e);
        }
        return null;
    }

    /**
     * 通过MultipartFile进行文件上传
     * @param bucketName: 存储桶名称
     * @param file: 文件
     * @return: java.lang.Boolean
     * @author 一蓑烟雨
     * @date 2022/9/3 8:23
     */
    public Boolean uploadFile(String bucketName, MultipartFile file){
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(file.getOriginalFilename())
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(),file.getSize(),-1)
                    .build());
            return true;
        } catch (Exception e) {
            log.error("使用MultipartFile进行文件上传异常：{}", e);
        }
        return false;
    }

    /**
     * 通过文件流进行文件上传
     * @param bucketName: 存储桶名称
     * @param fileName: 文件名
     * @param inputStream: 文件流
     * @return: java.lang.Boolean
     * @author 一蓑烟雨
     * @date 2022/9/9 17:20
     */
    public Boolean uploadFile(String bucketName, String fileName,InputStream inputStream){
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .stream(inputStream,inputStream.available(),-1)
                    .build());
            return true;
        } catch (Exception e) {
            log.error("通过文件流上传文件异常：{}", e);
        }
        return false;
    }

    /**
     * 创建目录或文件夹
     * @param bucketName: 存储桶名称
     * @param fileName: 目录或文件夹路径
     * @return: java.lang.Boolean
     * @author 一蓑烟雨
     * @date 2022/9/3 8:30
     */
    public Boolean createDir(String bucketName, String fileName){
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName).object(fileName)
                    .stream(new ByteArrayInputStream(new byte[]{}),0,-1)
                    .build());
            return true;
        } catch (Exception e) {
            log.error("在指定存储桶中创建目录或文件夹异常：{}", e);
        }
        return false;
    }

    /**
     * 获取文件信息
     * @param bucketName: 存储桶名称
     * @param fileName: 文件名称
     * @return: java.lang.String
     * @author 一蓑烟雨
     * @date 2022/9/3 8:33
     */
    public String getFileInfo(String bucketName, String fileName){
        try {
            return minioClient.statObject(StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .build()).toString();
        } catch (Exception e) {
            log.error("获取文件信息异常：{}", e);
        }
        return null;
    }

    /**
     * 拷贝指定文件
     * @param bucketName:  存储桶名称
     * @param objectName:  对象名称
     * @param stcBucketName:  目标存储桶名称
     * @param srcObjectName:  目标对象名称
     * @return: io.minio.ObjectWriteResponse
     * @author 一蓑烟雨
     * @date 2022/9/3 8:36
     */
    public ObjectWriteResponse copyFile(String bucketName, String objectName,
                                               String stcBucketName,String srcObjectName){
        try {
            return minioClient.copyObject(CopyObjectArgs.builder()
                    .source(CopySource.builder().bucket(bucketName).object(objectName).build())
                    .bucket(stcBucketName).object(srcObjectName)
                    .build());
        } catch (Exception e) {
            log.error("拷贝指定存储桶中的文件异常：{}", e);
        }
        return null;
    }

    /**
     * 删除指定文件
     * @param bucketName: 存储桶名称
     * @param objectName: 文件名称
     * @return: Boolean
     * @author 一蓑烟雨
     * @date 2022/9/3 8:39
     */
    public Boolean removeFile(String bucketName, String objectName){
        try {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
            return true;
        } catch (Exception e) {
            log.error("删除指定存储桶中的指定文件异常：{}", e);
        }
        return false;
    }

    /**
     * 批量删除指定文件
     * @param bucketName: 存储桶名称
     * @param keys: 文件名称集合
     * @return: Boolean
     * @author 一蓑烟雨
     * @date 2022/9/3 8:42
     */
    public boolean removeFiles(String bucketName, List<String> keys){
        try {
            List<DeleteObject> objects = new LinkedList<>();
            keys.forEach(key -> {
                objects.add(new DeleteObject(key));
                removeFile(bucketName,key);
            });
            return true;
        } catch (Exception e) {
            log.error("批量删除指定存储桶中的指定文件异常：{}", e);
        }
        return false;
    }

    /**
     * 获取文件路径，并设置过期时间
     * @param bucketName: 存储桶名称
     * @param objectName: 文件名称
     * @param expires: 过期时间(单位：秒)
     * @return: java.lang.String
     * @author 一蓑烟雨
     * @date 2022/9/3 8:47
     */
    public String getObjectUrl(String bucketName, String objectName,Integer expires){
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .expiry(expires).bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            log.error("获取文件链接异常：{}", e);
        }
        return null;
    }

    /**
     * 获取文件路径
     * @param bucketName: 存储桶名称
     * @param objectName: 文件名称
     * @return: java.lang.String
     * @author 一蓑烟雨
     * @date 2022/9/3 8:48
     */
    public String getObjectUrl(String bucketName, String objectName){
        try {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(bucketName).object(objectName).build());
        } catch (Exception e) {
            log.error("获取文件链接异常：{}", e);
        }
        return null;
    }

    /**
     * 将URLDecoder编码转成UTF8
     * @param str: 待转化字符串
     * @return: java.lang.String
     * @author 一蓑烟雨
     * @date 2022/9/3 8:51
     */
    public String getUTF8ByDecoder(String str){
        try {
            return URLDecoder.decode(str.replaceAll("%(?![0-9a-fA-F]{2})", "%25"),"UTF-8");
        } catch (UnsupportedEncodingException e) {
            log.error("获取文件链接异常：{}", e);
        }
        return null;
    }
}
